home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / nn.zip / TERM.C < prev    next >
C/C++ Source or Header  |  1989-12-31  |  27KB  |  1,571 lines

  1. /*
  2.  * terminal interface
  3.  */
  4.  
  5. #include <signal.h> 
  6. #include <errno.h>
  7. #include "config.h"
  8. #include "term.h"
  9. #include "keymap.h"
  10.  
  11. #ifdef RESIZING
  12. #include <sys/ioctl.h>            /* for TIOCGWINSZ */
  13.  
  14. int s_resized;
  15. #endif
  16.  
  17. export char *term_name;
  18. export int  show_current_time = 1;
  19. export int  conf_dont_sleep = 0;
  20. export int  prompt_length;
  21. export int  terminal_speed;
  22. export int  slow_speed = 1200;
  23. export int  any_message = 0;
  24. export int  flow_control = 1;
  25. export int  use_visible_bell = 1; /* if supported by terminal */
  26.  
  27. export unsigned char help_key = '?';
  28. export unsigned char comp1_key = SP;
  29. export unsigned char comp2_key = TAB;
  30. export unsigned char erase_key, kill_key;
  31. export unsigned char delword_key = CONTROL_('W');
  32.  
  33. static char bell_str[256] = "\007";
  34.  
  35. #ifdef USE_TERMINFO
  36.  
  37. #include <curses.h>
  38. #ifndef auto_left_margin
  39. #include <term.h>
  40. #endif
  41.  
  42. #define HAS_CAP(str) (str && *str)
  43.  
  44. extern char *tgoto();        /* some systems don't have this in term.h */
  45.  
  46. #else
  47.  
  48. #define USE_TERMCAP
  49.  
  50. char *tgoto();
  51. char PC;
  52. char *BC, *UP;
  53. short ospeed;
  54.  
  55. static char XBC[64], XUP[64];
  56. static char enter_ca_mode[64], exit_ca_mode[64];
  57. static char cursor_home[64];
  58. static char cursor_address[64];
  59. static char clear_screen[64];
  60. static char clr_eol[64];
  61. static char clr_eos[64];
  62. static char enter_standout_mode[64], exit_standout_mode[64];
  63. static char enter_underline_mode[64], exit_underline_mode[64];
  64. static char key_down[64], key_up[64], key_right[64], key_left[64];
  65.  
  66. int  magic_cookie_glitch;    /* magic cookie size */
  67.  
  68. #define putp(str)    tputs(str, 0, outc)
  69.  
  70. #define HAS_CAP(str)    (*str)
  71.  
  72. static outc(c)
  73. {
  74.     putchar(c);
  75. }
  76.  
  77. #endif    /* USE_TERMCAP */
  78.  
  79. int  Lines, Columns;    /* screen size */
  80. int  cookie_size;    /* size of magic cookie */
  81. int  two_cookies;    /* space needed to enter&exit standout mode */
  82. int  STANDOUT;        /* terminal got standout mode */
  83. int  WRAP;        /* terminal got automatic margins */
  84.  
  85. #ifdef HAVE_TERMIO
  86.  
  87. #define KEY_BURST 50    /* read bursts of 50 chars (or timeout after 100 ms) */
  88.  
  89. #ifdef USE_TERMCAP
  90. #include <termio.h>
  91. #endif
  92.  
  93. #undef CBREAK
  94.  
  95. static struct termio norm_tty, raw_tty;
  96.  
  97. #define    IntrC    norm_tty.c_cc[VINTR]
  98. #define    EraseC    norm_tty.c_cc[VERASE]
  99. #define KillC    norm_tty.c_cc[VKILL]
  100. #define SuspC    CONTROL_('Z')    /* norm_tty.c_cc[SWTCH] */
  101.  
  102. #else    /* V7/BSD TTY DRIVER */
  103.  
  104. #include <sgtty.h>
  105.  
  106. static struct sgttyb norm_tty, raw_tty;
  107. static struct tchars norm_chars;
  108.  
  109. #define    IntrC    norm_chars.t_intrc
  110. #define    EraseC    norm_tty.sg_erase
  111. #define KillC    norm_tty.sg_kill
  112.  
  113. #ifdef TIOCGLTC
  114. static struct     ltchars spec_chars;
  115. #define SuspC    spec_chars.t_suspc
  116. #else
  117. #define    SuspC    CONTROL_('Z')
  118. #endif
  119.  
  120. #endif
  121.  
  122. #ifdef USE_TERMCAP
  123.  
  124. opt_cap(cap, buf)
  125. char *cap, *buf;
  126. {
  127.     char *tgetstr();
  128.     
  129.     *buf = NUL;
  130.     return tgetstr(cap, &buf) != NULL;
  131. }
  132.  
  133. get_cap(cap, buf)
  134. char *cap, *buf;
  135. {
  136.     if (!opt_cap(cap, buf)) 
  137.     user_error("TERMCAP entry for %s has no '%s' capability",
  138.            term_name, cap);
  139. }           
  140.  
  141. #endif  /* USE_TERMCAP */
  142.  
  143. static int multi_keys = 0;
  144.  
  145. static struct multi_key {
  146.     char *cur_key;
  147.     char *keys;
  148.     int code;
  149. } multi_key_list[MULTI_KEYS];
  150.  
  151. enter_multi_key(code, keys)
  152. int code;
  153. char *keys;
  154. {
  155.     register i;
  156.     
  157.     /* lookup code to see if it is already defined... */
  158.     for (i = 0; i < multi_keys; i++)
  159.     if (multi_key_list[i].code == code)
  160.         goto replace_key;
  161.     
  162.     i = multi_keys++;
  163.     
  164.     /* now i points to matching or empty slot */
  165.     if (i >= MULTI_KEYS) {
  166.     /* should never happen */
  167.     log_entry('E', "too many multi keys");
  168.     return;
  169.     }
  170.     
  171.  replace_key:
  172.  
  173.     multi_key_list[i].keys = keys;
  174.     multi_key_list[i].code = code;
  175. }
  176.  
  177. dump_multi_keys()
  178. {
  179.     register i;
  180.     register char *cp;
  181.     extern char *key_name();
  182.     
  183.     clrdisp();
  184.     pg_init(0, 1);
  185.     
  186.     for (i = 0; i < multi_keys; i++) {
  187.     if (pg_next() < 0) break;
  188.     printf("%d\t%s\t", i, key_name(multi_key_list[i].code));
  189.     for (cp = multi_key_list[i].keys; *cp; cp++) {
  190.         putchar(SP);
  191.         fputs(key_name(*cp), stdout);
  192.     }
  193.     }
  194.  
  195.     pg_end();
  196. }
  197.  
  198.     
  199. #ifdef RESIZING
  200.  
  201. sig_type catch_winch()
  202. {
  203.     struct winsize winsize;
  204.  
  205.     (void) signal(SIGWINCH, catch_winch);
  206.     if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
  207.     && (winsize.ws_row != Lines || winsize.ws_col != Columns)) { 
  208.     Lines = winsize.ws_row;
  209.     Columns = winsize.ws_col;
  210.     s_redraw = 1;
  211.     s_resized = 1;
  212.     }
  213. }
  214. #endif /* RESIZING */
  215.  
  216. #ifdef SV_INTERRUPT
  217. #ifdef NO_SIGINTERRUPT
  218. static siginterrupt(signo, on)
  219. {
  220.   struct sigvec sv;
  221.   sv.sv_handler = signal (signo, SIG_DFL);
  222.   sv.sv_mask = 0;
  223.   sv.sv_flags = on ? SV_INTERRUPT : 0;
  224.   sigvec (signo, &sv, 0);
  225. }
  226. #endif
  227. #endif
  228.  
  229. init_term()
  230. {
  231. #ifdef USE_TERMCAP
  232.     char tbuf[1024];
  233. #endif
  234.     
  235.     if ((term_name = getenv("TERM")) == NULL)
  236.     user_error("No TERM variable in enviroment");
  237.  
  238. #ifdef HAVE_TERMIO
  239.     ioctl(0, TCGETA, &norm_tty);
  240. #else    
  241.     ioctl(0, TIOCGETP, &norm_tty);
  242. #endif
  243.     
  244. #ifdef USE_TERMINFO
  245.     setupterm((char *)NULL, 1, (int *)NULL);
  246.     Columns = columns;
  247.     Lines = lines;
  248.     cookie_size = magic_cookie_glitch;
  249.     WRAP = auto_right_margin;
  250.     if (use_visible_bell && HAS_CAP(flash_screen)) 
  251.     strcpy(bell_str, flash_screen);
  252.     else if (HAS_CAP(bell)) 
  253.     strcpy(bell_str, bell);
  254.     if (! HAS_CAP(cursor_home))
  255.     cursor_home = copy_str(tgoto(cursor_address, 0, 0));
  256. #else
  257.     
  258.     if (tgetent(tbuf, term_name) <= 0)
  259.     user_error("Unknown terminal type: %s", term_name);
  260.     
  261.     if (opt_cap("bc", XBC)) BC = XBC;
  262.     if (opt_cap("up", XUP)) UP = XUP;
  263.     opt_cap("pc", cursor_address);    /* temp. usage */
  264.     PC = cursor_address[0];
  265.     
  266.     get_cap("cm", cursor_address);
  267.     if (!opt_cap("ho", cursor_home))
  268.     strcpy(cursor_home, tgoto(cursor_address, 0, 0));
  269.  
  270.     get_cap("cl", clear_screen);
  271.     get_cap("ce", clr_eol);
  272.     opt_cap("cd", clr_eos);
  273.     
  274. #ifdef RESIZING
  275.     {
  276.     struct winsize winsize;
  277.  
  278.     if (ioctl(0, TIOCGWINSZ, &winsize) >= 0
  279.         && winsize.ws_row != 0 && winsize.ws_col != 0) { 
  280.         Lines = winsize.ws_row;
  281.         Columns = winsize.ws_col;
  282.         (void) signal(SIGWINCH, catch_winch);
  283. #ifdef SV_INTERRUPT
  284.         siginterrupt(SIGWINCH, 1);    /* make read from tty interruptable */
  285. #endif /* SV_INTERRUPT */
  286.     }
  287.     }
  288.     if (Lines == 0 || Columns == 0) {
  289. #endif /* RESIZING */
  290.     Lines = tgetnum("li");
  291.     Columns = tgetnum("co");
  292. #ifdef RESIZING
  293.     }
  294. #endif /* RESIZING */
  295.  
  296.     opt_cap("so", enter_standout_mode);
  297.     opt_cap("se", exit_standout_mode);
  298.     
  299.     opt_cap("us", enter_underline_mode);
  300.     opt_cap("ue", exit_underline_mode);
  301.  
  302.     opt_cap("kd", key_down);
  303.     opt_cap("ku", key_up);
  304.     opt_cap("kr", key_right);
  305.     opt_cap("kl", key_left);
  306.  
  307.     cookie_size = tgetnum("sg");
  308.     
  309.     WRAP = tgetflag("am");
  310.     
  311.     opt_cap("ti", enter_ca_mode);
  312.     opt_cap("te", exit_ca_mode);
  313.  
  314.     if (!use_visible_bell || !opt_cap("vb", bell_str))
  315.     if (!opt_cap("bl", bell_str)) 
  316.         strcpy(bell_str, "\007");
  317.     
  318. #endif /* !USE_TERMINFO */
  319.     
  320.     STANDOUT = HAS_CAP(enter_standout_mode);
  321.     if (STANDOUT) {
  322.     if (cookie_size < 0) cookie_size = 0;
  323.     two_cookies = 2 * cookie_size;
  324.     } else
  325.     cookie_size = two_cookies = 0;
  326.     
  327.     
  328.     raw_tty = norm_tty;
  329.     
  330. #ifdef HAVE_TERMIO
  331.     raw_tty.c_iflag &= ~(BRKINT|INLCR|ICRNL|IGNCR);
  332.     raw_tty.c_iflag |= IGNBRK|IGNPAR|ISTRIP;
  333.     raw_tty.c_oflag &= ~OPOST;
  334.     raw_tty.c_lflag &= ~(ISIG|ICANON|XCASE|ECHO|NOFLSH);
  335.     
  336.     /* read a maximum of 10 characters in one burst; timeout in 1-200 ms */
  337.     raw_tty.c_cc[VEOF] = KEY_BURST;
  338.     raw_tty.c_cc[VEOL] = ((raw_tty.c_cflag & CBAUD) > B1200) ? 1 : 2;
  339.     set_term_speed((unsigned)(raw_tty.c_cflag & CBAUD));
  340. #else    
  341.     ioctl(0, TIOCGETC, &norm_chars);
  342.     
  343. #ifdef TIOCGLTC
  344.     ioctl(0, TIOCGLTC, &spec_chars);
  345. #endif
  346.     
  347.     ospeed = norm_tty.sg_ospeed;
  348.     set_term_speed((unsigned)ospeed);
  349.     
  350.     raw_tty.sg_flags &= ~ECHO;
  351. #ifdef CBREAK
  352. #ifdef SV_INTERRUPT            /* make read from tty interruptable */
  353.     siginterrupt(SIGTSTP, 1);        /* this is necessary to redraw screen */
  354. #endif
  355.     raw_tty.sg_flags |= CBREAK;
  356. #else
  357.     raw_tty.sg_flags |= RAW;
  358. #endif
  359.  
  360. #ifdef SV_INTERRUPT
  361.     siginterrupt(SIGALRM, 1);        /* make read from tty interruptable */
  362. #endif
  363. #endif
  364.  
  365.     if (HAS_CAP(key_down))  enter_multi_key(K_down_arrow, key_down);
  366.     if (HAS_CAP(key_up)) enter_multi_key(K_up_arrow, key_up);
  367.     if (HAS_CAP(key_right)) enter_multi_key(K_right_arrow, key_right);
  368.     if (HAS_CAP(key_left)) enter_multi_key(K_left_arrow, key_left);
  369.     
  370.     erase_key = EraseC;
  371.     kill_key  = KillC;
  372.  
  373.     visual_on();
  374. }
  375.  
  376. static unsigned sp_table[] = {
  377.     B9600, 960,
  378. #ifdef B19200
  379.     B19200, 1920,
  380. #else
  381. #ifdef EXTA
  382.     EXTA, 1920,
  383. #endif
  384. #endif
  385. #ifdef B38400
  386.     B38400, 3840,
  387. #else
  388. #ifdef EXTB
  389.     EXTB, 3840,
  390. #endif
  391. #endif
  392.     B1200, 120,
  393.     B2400, 240,
  394.     B4800, 480,
  395.     B300,   30,
  396.     0,    0
  397. };
  398.  
  399. static set_term_speed(sp)
  400. register unsigned long sp;
  401. {
  402.     register unsigned *tp;
  403.  
  404.     for (tp = sp_table; *tp; tp += 2)
  405.     if (*tp == sp) {
  406.         terminal_speed = tp[1];
  407.         return;
  408.     }
  409.     
  410.     terminal_speed = 30;
  411. }
  412.  
  413. home()
  414. {
  415.     putp(cursor_home);
  416. }
  417.  
  418. static int curxy_c = -1, curxy_l, savxy_c = -1, savxy_l;
  419.  
  420. save_xy()
  421. {
  422.     savxy_c = curxy_c; savxy_l = curxy_l;
  423. }
  424.  
  425. restore_xy()
  426. {
  427.     if (savxy_c < 0) return;
  428.     gotoxy(savxy_c, savxy_l); fl;
  429. }
  430.  
  431. gotoxy(c, l)
  432. int c, l;
  433. {
  434.     curxy_c = c; curxy_l = l;
  435.     putp(tgoto(cursor_address, c, l));
  436. }
  437.  
  438. clrdisp()
  439. {
  440. #ifdef USE_TERMINFO
  441.     putp(clear_screen);        /* tputs is broken on UNISYS I've been told */
  442. #else
  443.     tputs(clear_screen, Lines, outc);
  444. #endif
  445.     curxy_c = savxy_c = -1;
  446. }
  447.  
  448. clrline()
  449. {
  450.     putp(clr_eol);
  451.     fl;
  452. }
  453.  
  454. clrpage(lineno)
  455. register int lineno;
  456. {
  457.     register int olineno= lineno;
  458.  
  459.     if (HAS_CAP(clr_eos)) {
  460. #ifdef USE_TERMINFO
  461.     putp(clr_eos);
  462. #else    
  463.     tputs(clr_eos, Lines - lineno, outc);
  464. #endif
  465.     } else {
  466.     clrline();
  467.     lineno++;
  468.     for (; lineno < Lines; lineno++) {
  469.         gotoxy(0, lineno);
  470.         putp(clr_eol);
  471.     }
  472.     gotoxy(0, olineno);
  473.     fl;
  474.     }
  475. }
  476.  
  477. static char so_buf[512], *so_p;
  478. static int so_c, so_l, so_b, so_active = 0;
  479.  
  480. so_gotoxy(c, l, blank)
  481. {
  482.     if (!STANDOUT && c >= 0) {
  483.     if (l >= 0) gotoxy(c, l);
  484.     return 0;
  485.     }
  486.     
  487.     so_active++;
  488.     so_c = c;
  489.     so_l = l;
  490.     so_b = blank;
  491.     so_p = so_buf;
  492.     *so_p = NUL;
  493.  
  494.     return 1;    /* not really true if not standout & c < 0 */
  495. }
  496.  
  497. /*VARARGS*/
  498. so_printf(va_alist)
  499. va_dcl
  500. {
  501.     va_list ap;
  502.     
  503.     va_start(ap);
  504.     so_vprintf(va_args1toN);
  505.     va_end(ap);
  506. }
  507.  
  508. so_vprintf(va_tail)
  509. va_tdcl
  510. {
  511.     char *fmt;
  512.     
  513.     fmt = va_arg1(char *);
  514.  
  515.     if (!so_active) {
  516.     vprintf(fmt, va_args2toN);
  517.     return;
  518.     }
  519.     
  520.     vsprintf(so_p, fmt, va_args2toN);
  521.     while (*so_p) so_p++;
  522. }
  523.  
  524. so_end()
  525. {
  526.     int len;
  527.  
  528.     if (!so_active) return;
  529.  
  530.     if (so_l >= 0) {
  531.     
  532.     len = so_p - so_buf + two_cookies; 
  533.  
  534.     if (so_c < 0)
  535.         so_c = Columns - len - 2;
  536.     if (so_c < 0) so_c = 0;
  537.  
  538.     if (len + so_c >= Columns) {
  539.         len = Columns - so_c - two_cookies;
  540.         so_buf[len] = NUL;
  541.     }
  542.         
  543.     if (cookie_size) {
  544.         gotoxy(so_c + len - cookie_size, so_l);
  545.         putp(exit_standout_mode);
  546.     }    
  547.     
  548.     gotoxy(so_c, so_l);
  549.  
  550.     }
  551.     
  552.     if ((so_b & 1) && (!STANDOUT || !cookie_size)) putchar(SP);
  553.  
  554.     if (STANDOUT) putp(enter_standout_mode);
  555.     
  556.     fputs(so_buf, stdout);
  557.     
  558.     if (STANDOUT) putp(exit_standout_mode);
  559.     
  560.     if ((so_b & 2) && (!STANDOUT || !cookie_size)) putchar(SP);
  561.  
  562.     so_active = 0;
  563. }
  564.  
  565.  
  566. /*VARARGS*/
  567. so_printxy(va_alist)
  568. va_dcl
  569. {
  570.     va_list ap;
  571.     int k, l;
  572.  
  573.     va_start(ap);
  574.     
  575.     k = va_arg1(int);
  576.     l = va_arg2(int);
  577.     
  578.     so_gotoxy(k, l, 0);
  579.     so_vprintf(va_args3toN);
  580.     so_end();
  581.  
  582.     va_end(ap);
  583. }
  584.  
  585. underline(on)
  586. {
  587.     if (cookie_size) return 0;
  588.     if (! HAS_CAP(enter_underline_mode)) return 0;
  589.     putp(on ? enter_underline_mode : exit_underline_mode);
  590.     return 1;
  591. }
  592.  
  593. highlight(on)
  594. {
  595.     if (cookie_size) return 0;
  596.     if (! HAS_CAP(enter_standout_mode)) return 0;
  597.     putp(on ? enter_standout_mode : exit_standout_mode);
  598.     return 1;
  599. }
  600.  
  601. static int is_visual = 0;
  602.  
  603. visual_on()
  604. {
  605.     if (HAS_CAP(enter_ca_mode)) {
  606.     putp(enter_ca_mode);
  607.     is_visual = 1;
  608.     }
  609. }
  610.  
  611. visual_off()
  612. {
  613.     if (is_visual && HAS_CAP(exit_ca_mode)) putp(exit_ca_mode), fl;
  614.     is_visual = 0;
  615. }
  616.   
  617. static int is_raw = 0;
  618. static int must_set_raw = 1;
  619.  
  620. #ifndef CBREAK
  621. raw()
  622. {
  623.     if (!flow_control) {
  624.     if (!must_set_raw) return;
  625.     must_set_raw = 0;
  626.     }
  627.     
  628.     if (is_raw) return;
  629.  
  630. #ifdef HAVE_TERMIO
  631.     ioctl(0, TCSETAF, &raw_tty);
  632. #else
  633.     ioctl(0, TIOCSETP, &raw_tty);
  634. #endif
  635.     is_raw++;
  636. }
  637.  
  638. no_raw()
  639. {
  640.     if (!flow_control) return 0;
  641.     
  642.     if (!is_raw) return 0;
  643.  
  644. #ifdef HAVE_TERMIO
  645.     ioctl(0, TCSETAF, &norm_tty);
  646. #else
  647.     ioctl(0, TIOCSETP, &norm_tty);
  648. #endif
  649.     is_raw = 0;
  650.  
  651.     return 1;
  652. }
  653.  
  654. unset_raw()
  655. {
  656.     int oflow = flow_control;
  657.     int was_raw;
  658.     
  659.     flow_control = 1;
  660.     was_raw = no_raw();
  661.     flow_control = oflow;
  662.     if (!flow_control)
  663.     must_set_raw = 1;
  664.     return was_raw;
  665. }
  666.  
  667. #else /* not CBREAK */
  668. raw()
  669. {
  670.     if (is_raw == 1)
  671.     return;
  672.     is_raw = 1;
  673.     ioctl(0, TIOCSETP, &raw_tty);    /* no TERMIO ??? */
  674. }
  675.  
  676. no_raw()
  677. {
  678.     return 0;
  679. }
  680.  
  681. unset_raw()
  682. {
  683.     if (is_raw == 0)
  684.     return 0;
  685.     ioctl(0, TIOCSETP, &norm_tty);    /* no TERMIO ??? */
  686.     is_raw = 0;
  687.     return 1;
  688. }
  689. #endif /* CBREAK */
  690.  
  691. static int do_flush_input = 0;
  692.  
  693. flush_input()
  694. {
  695. #ifdef HAVE_TERMIO
  696.     ioctl(0, TCFLSH, 0);
  697.     do_flush_input = 1;
  698. #else
  699. #ifdef FREAD
  700.     int arg = FREAD;
  701.     ioctl(0, TIOCFLUSH, &arg);
  702. #else
  703.     ioctl(0, TIOCFLUSH, 0);
  704. #endif
  705. #endif
  706. }
  707.     
  708. int enable_stop = 1;
  709.  
  710. #ifndef KEY_BURST
  711.  
  712. static int alarm_on = 0;
  713.  
  714. static mk_timeout()
  715. {
  716.     alarm_on = 0;
  717. }
  718.  
  719. #endif
  720.  
  721. static int do_macro_processing = 1;
  722.  
  723. get_c()
  724. {
  725.     unsigned char c;
  726.     int any_multi, key_cnt, mc;
  727.     register struct multi_key *mk;
  728.     register int i;
  729. #ifdef KEY_BURST
  730.     static char cbuf[KEY_BURST], *cp;
  731.     static int n = 0;
  732.  
  733.     if (do_flush_input) {
  734.     do_flush_input = 0;
  735.     n = 0;
  736.     }
  737. #else
  738.     int n;
  739.     unsigned char first_key;
  740. #endif
  741.  
  742.  next_key:
  743.     if (s_hangup)
  744.     return K_interrupt;
  745.  
  746. #ifdef RESIZING
  747.     if (s_resized) {
  748.     s_resized = 0;
  749.     return GETC_COMMAND | K_REDRAW;
  750.     }
  751. #endif
  752.     
  753.     if (do_macro_processing)
  754.     switch (m_getc(&mc)) {
  755.      case 0:
  756.         break;
  757.      case 1:
  758.         return mc;
  759.      case 2:
  760.         return K_interrupt;
  761.     }
  762.     
  763.     for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++)
  764.     mk->cur_key = mk->keys;
  765.     key_cnt = 0;
  766.  
  767. #ifdef KEY_BURST
  768.     if (n <= 0) {
  769.     n = read(0, cbuf, KEY_BURST);
  770.     if (n < 0 && errno != EINTR) s_hangup++;
  771.     if (n <= 0) return K_interrupt;
  772.     cp = cbuf;
  773.     }
  774.     
  775.     while (--n >= 0) {
  776.     c = *cp++;
  777. #else    
  778.  
  779.     while ((n = read(0, &c, 1)) > 0) {
  780.     c &= 0177;    /* done by ISTRIP on USG systems */
  781. #endif
  782.     
  783.     if (c == CONTROL_('Q') || c == CONTROL_('S'))
  784.         continue;
  785.     
  786.     any_multi = 0;
  787.     for (i = multi_keys, mk = multi_key_list; --i >= 0; mk++)
  788.         if (mk->cur_key) {
  789.         if (*(mk->cur_key)++ == c) {
  790.             if (*(mk->cur_key) == NUL) {
  791.             c = mk->code;
  792.             goto got_char;
  793.             }
  794.             any_multi++;
  795.         } else
  796.             mk->cur_key = NULL;
  797.         }
  798.  
  799.     if (any_multi) {
  800. #ifndef KEY_BURST
  801.         if (key_cnt == 0) {
  802.         first_key = c;
  803.         alarm_on = 1;
  804.         signal(SIGALRM, mk_timeout);
  805.         MICRO_ALARM();
  806.         }
  807. #endif
  808.         key_cnt++;
  809.         continue;
  810.     }
  811.     if (key_cnt == 0) 
  812.         goto got_char;
  813.     
  814.     ding();
  815.     flush_input();
  816.     goto next_key;
  817.     }
  818.  
  819. #ifdef CBREAK
  820.     if (s_redraw) {
  821.     s_redraw = 0;
  822.     return GETC_COMMAND | K_REDRAW;
  823.     }
  824. #endif
  825.  
  826. #ifndef KEY_BURST
  827.     if (n < 0) {
  828.     if (errno != EINTR) s_hangup++;
  829.     return K_interrupt;
  830.     }
  831. #endif
  832.     
  833. #ifdef RESIZING
  834.     if (s_resized) {
  835.     s_resized = 0;
  836.     return GETC_COMMAND | K_REDRAW;
  837.     }
  838. #endif
  839.  
  840. #ifndef KEY_BURST    
  841.     if (n < 0 && key_cnt)
  842.     c = first_key;
  843. #endif
  844.  
  845. got_char:
  846.  
  847. #ifndef KEY_BURST
  848.     if (alarm_on) {
  849.     alarm(0);
  850.     alarm_on = 0;
  851.     }
  852. #endif
  853.     
  854.     c = global_key_map[c];
  855.     
  856. #ifndef CBREAK
  857.     if (c == SuspC) {
  858.     if (!enable_stop) goto next_key;
  859.     if (suspend_nn())
  860.         return GETC_COMMAND | K_REDRAW;
  861.     else
  862.         goto next_key;
  863.     }
  864.  
  865.     if (c == IntrC) c = K_interrupt;
  866. #endif
  867.     return c;
  868. }
  869.  
  870.  
  871. /*
  872.  * read string with completion, pre-filling, and break on first char
  873.  *
  874.  *    dflt        is a string that will be use as default value if the
  875.  *            space bar is hit as the first character.
  876.  *
  877.  *    prefill        pre-fill the buffer with .... and print it
  878.  *
  879.  *    break_chars    return immediately if one of these characters
  880.  *            is entered as the first character.
  881.  *
  882.  *    completion     is a function that will fill the buffer with a value
  883.  *            see the group_completion and file_completion routines 
  884.  *            for examples.
  885.  */
  886.  
  887. char *get_s(dflt, prefill, break_chars, completion)
  888. char *dflt, *prefill, *break_chars;
  889. int (*completion)();
  890. {
  891.     static char buf[GET_S_BUFFER];
  892.     register char *cp;
  893.     register int i, c, lastc;
  894.     char *ret_val = buf;
  895.     int comp_used, comp_len;
  896.     int ostop, max, did_help;
  897.     int hit_count;
  898.     
  899.     switch (m_gets(buf)) {
  900.      case 0:
  901.     break;
  902.      case 1:
  903.     return buf;
  904.      case 2:
  905.     return NULL;
  906.     }
  907.     
  908.     ostop = enable_stop;
  909.     enable_stop = 0;
  910.     do_macro_processing = 0;
  911.     hit_count = 0;
  912.     
  913.     max = Columns - prompt_length;
  914.     
  915.     if (max >= FILENAME) max = FILENAME-1;
  916.  
  917.     i = comp_len = comp_used = did_help = 0;
  918.  
  919.     if (prefill && prefill[0]) {
  920.     while (c = *prefill++) {
  921.         if (i == max) break;
  922.         
  923.         putchar(c);
  924.         buf[i] = c;
  925.         i++;
  926.     }
  927.     fl;
  928.     }        
  929.  
  930.     if (dflt && *dflt == NUL)
  931.     dflt = NULL;
  932.  
  933.     if (break_chars && *break_chars == NUL)
  934.     break_chars = NULL;
  935.  
  936.     c = NUL;
  937.     for(;;) {
  938.     lastc = c;
  939.     c = get_c();
  940.     if (c & GETC_COMMAND) continue;
  941.  
  942.      kill_prefill_hack:
  943.  
  944.     hit_count++;
  945.  
  946.     if (i == 0) {
  947.         if (c == comp1_key && dflt) {
  948.         while ((c = *dflt++) != NUL && i < max) {
  949.             putchar(c);
  950.             buf[i] = c;
  951.             i++;
  952.         }
  953.         fl;
  954.         dflt = NULL;
  955.         continue;
  956.         }
  957.         if (cp = break_chars) {
  958.         while (*cp)
  959.             if (*cp++ == c) {
  960.             buf[0] = c;
  961.             buf[1] = NUL;
  962.             goto out;
  963.             }
  964.         }
  965.     }
  966.  
  967.     if (completion != NO_COMPLETION) {
  968.         if (comp_used && c == erase_key) {
  969.         (*completion)(buf, -1);
  970.         if (did_help) { clrmsg(i); did_help = 0; }
  971.         if (comp_len) {
  972.             i -= comp_len;
  973.             while (--comp_len >= 0) putchar(BS);
  974.             clrline();
  975.         }
  976.         comp_len = comp_used = 0;
  977.         if (lastc == help_key) goto no_completion;
  978.         continue;
  979.         }
  980.  
  981.         if (c == comp1_key || c == comp2_key || c == help_key) {
  982.         if (!comp_used || c == comp2_key || 
  983.             (c == help_key && lastc != c)) {
  984.             buf[i] = NUL;
  985.             if ((comp_used = (*completion)(buf, i)) == 0) {
  986.             ding();
  987.             continue;
  988.             }
  989.             if (comp_used < 0) {
  990.             comp_used = 0;
  991.             goto no_completion;
  992.             }
  993.             comp_len = 0;
  994.         }
  995.         if (c == help_key) {
  996.             if ((*completion)((char *)NULL, 1)) {
  997.             gotoxy(prompt_length+i, prompt_line); fl;
  998.             did_help = 1;
  999.             }
  1000.             continue;
  1001.         }
  1002.         
  1003.         if (comp_len) {
  1004.             i -= comp_len;
  1005.             while (--comp_len >= 0) putchar(BS);
  1006.             clrline();
  1007.             comp_len = 0;
  1008.         }
  1009.         
  1010.         switch ( (*completion)((char *)NULL, 0) ) {
  1011.  
  1012.          case 0:    /* no possible completion */
  1013.             comp_used = 0;
  1014.             ding();
  1015.             continue;
  1016.             
  1017.          case 2:    /* whole new alternative */
  1018.             while (--i >= 0) putchar(BS);
  1019.             clrline();
  1020.             /* fall thru */
  1021.  
  1022.          case 1:    /* completion */
  1023.             comp_len = i;
  1024.             while (c = buf[i]) {
  1025.             if (i == max) break;
  1026.             putchar(c);
  1027.             i++;
  1028.             }
  1029.             fl;
  1030.             comp_len = i - comp_len;
  1031.             continue;
  1032.         }
  1033.         }
  1034.  
  1035.         if (comp_used) {
  1036.         (*completion)(buf, -1);
  1037.         if (did_help) clrmsg(i);
  1038.         comp_len = comp_used = 0;
  1039.         }
  1040.     }
  1041.     
  1042.      no_completion:
  1043.  
  1044.     if (c == CR || c == NL) {
  1045.         buf[i] = NUL;
  1046.         break;
  1047.     }
  1048.     
  1049.     if (c == erase_key) {
  1050.         if (i <= 0) continue;
  1051.         i--;
  1052.         putchar(BS);
  1053.         putchar(' ');
  1054.         putchar(BS);
  1055.         fl;
  1056.         continue;
  1057.     }
  1058.  
  1059.     if (c == delword_key) {
  1060.         if (i <= 0) continue;
  1061.         buf[i-1] = 'X';
  1062.         while (i > 0 && isalnum(buf[i-1])) { putchar(BS); i--; }
  1063.         clrline();
  1064.         continue;
  1065.     }
  1066.     
  1067.     if (c == kill_key) {
  1068.         while (i > 0) { putchar(BS); i--; }
  1069.         clrline();
  1070.         if (hit_count == 1 && dflt) {
  1071.         c = comp1_key;
  1072.         goto kill_prefill_hack;
  1073.         }
  1074.         continue;
  1075.     }
  1076.     
  1077.     if (c == K_interrupt) {
  1078.         ret_val = NULL;
  1079.         break;
  1080.     }
  1081.     
  1082.     if (!isascii(c) || !isprint(c)) continue;
  1083.     
  1084.     if (i == max) continue;
  1085.  
  1086.     if (i > 0 && buf[i-1] == '/' && (c == '/' || c == '+')) {
  1087.         while (i > 0) { putchar(BS); i--; }
  1088.         clrline();
  1089.     }        
  1090.  
  1091.     putchar(c);
  1092.     fl;
  1093.  
  1094.     buf[i] = c;
  1095.     i++;
  1096.     }
  1097.  out:
  1098.     enable_stop = ostop;
  1099.     do_macro_processing = 1;
  1100.     return ret_val;
  1101. }
  1102.  
  1103. export int list_offset = 0;
  1104.  
  1105. list_completion(str)
  1106. char *str;
  1107. {
  1108.     static int cols, line;
  1109.     
  1110.     if (str == NULL) {
  1111.     cols = Columns;
  1112.     line = prompt_line + 1;
  1113.     
  1114.     gotoxy(0, line);
  1115.     clrpage(line);
  1116.     return 1;
  1117.     }
  1118.  
  1119.     str += list_offset;
  1120.     
  1121.     for (;;) {
  1122.     cols -= strlen(str);
  1123.     if (cols >= 0) {
  1124.         printf("%s%s", str, cols > 0 ? " " : "");
  1125.         cols--;
  1126.         return 1;
  1127.     }
  1128.     if (line >= Lines - 1) return 0;
  1129.     line++;
  1130.     cols = Columns;
  1131.     gotoxy(0, line);
  1132.     if (line == Lines - 1) cols--;
  1133.     }
  1134. }
  1135.  
  1136. yes(must_answer)
  1137. int must_answer;
  1138. {
  1139.     int c, help = 1, in_macro = 0;
  1140.  
  1141.     switch (m_yes()) {
  1142.      case 0:
  1143.     break;
  1144.      case 1:
  1145.     return 0;
  1146.      case 2:
  1147.     return 1;
  1148.      case 3:
  1149.     do_macro_processing = 0;
  1150.         in_macro++;
  1151.     break;
  1152.     }
  1153.  
  1154.     for (;;) {
  1155.     if (!is_raw) {
  1156.         raw();
  1157.         c = get_c();
  1158.         unset_raw();
  1159.     } else
  1160.         c = get_c();
  1161.  
  1162.     if (c == 'y' || c == 'Y') {
  1163.         c = 1;
  1164.         break;
  1165.     }
  1166.     
  1167.     if (must_answer == 0 && (c == SP || c == CR || c == NL)) break;
  1168.     if (c == 'n' || c == 'N') {
  1169.         c = 0;
  1170.         break;
  1171.     }    
  1172.     if (c == K_interrupt) {
  1173.         c = -1;
  1174.         break;
  1175.     }
  1176.     if (help) {
  1177.         fputs(" y=YES n=NO", stdout);
  1178.         prompt_length += 11;
  1179.         help = 0;
  1180.     }
  1181.     }
  1182.  
  1183.     if (in_macro) {
  1184.     if (c < 0) m_break();
  1185.     do_macro_processing = 1;
  1186.     }
  1187.     
  1188.     return c;
  1189. }
  1190.  
  1191.  
  1192. ding()
  1193. {
  1194.     putp(bell_str);
  1195.     fl;
  1196. }
  1197.  
  1198.  
  1199. display_file(name, modes)
  1200. char *name;
  1201. int modes;
  1202. {
  1203.     FILE *f;
  1204.     register c, stand_on;
  1205.     int linecnt, headln_cnt, hdline, no_conf;
  1206.     char headline[128];
  1207.     
  1208.     headline[0] = 0;
  1209.     hdline = 0;
  1210.     no_conf = 0;
  1211.     
  1212.     headln_cnt = -1;
  1213.     
  1214.     if (modes & CLEAR_DISPLAY) {
  1215.     gotoxy(0,0);
  1216.     clrdisp();
  1217.     }
  1218.     
  1219.     linecnt = Lines - 1;
  1220.  
  1221. chain:    
  1222.  
  1223.     f = open_file(relative(lib_directory, name), OPEN_READ);
  1224.     if (f == NULL)
  1225.     printf("\r\n\nFile %s is not available\n\n", name);
  1226.     else {
  1227.     stand_on = 0;
  1228.     
  1229.     while ((c = getc(f)) != EOF) {
  1230. #ifdef HAVE_JOBCONTROL
  1231.         if (s_redraw) {
  1232.         no_conf = 1;
  1233.         break;
  1234.         }
  1235. #endif        
  1236.         no_conf = 0;
  1237.         if (c == '\1') {
  1238.         if (STANDOUT) {
  1239.             putp(stand_on ? exit_standout_mode : enter_standout_mode);
  1240.             stand_on = !stand_on;
  1241.         }
  1242.         continue;
  1243.         }
  1244.         if (c == '\2') {
  1245.         headln_cnt = 0;
  1246.         continue;
  1247.         }
  1248.         if (c == '\3') {
  1249.         headln_cnt = 0;
  1250.         while ((c = getc(f)) != EOF && c != NL)
  1251.             headline[headln_cnt++] = c;
  1252.         headline[headln_cnt++] = NUL;
  1253.         name = headline;
  1254.         fclose(f);
  1255.         goto chain;
  1256.         }
  1257.  
  1258.         if (headln_cnt >= 0)
  1259.         headline[headln_cnt++] = c;
  1260.  
  1261.         if (hdline) {
  1262.         puts(headline);
  1263.         putchar(CR);
  1264.         hdline = 0;
  1265.         linecnt--;
  1266.         }
  1267.         
  1268.         putchar(c);
  1269.         if (c == NL) {
  1270.         putchar(CR);
  1271.         if (headln_cnt >= 0) {
  1272.             headline[--headln_cnt] = 0;
  1273.             headln_cnt = -1;
  1274.         }
  1275.         if (--linecnt == 0) {
  1276.             no_conf = 1;
  1277.             if (any_key(0) == K_interrupt) 
  1278.             break;
  1279.             linecnt = Lines - 1;
  1280.             if (modes & CLEAR_DISPLAY) {
  1281.             gotoxy(0,0);
  1282.             clrdisp();
  1283.             }
  1284.             hdline = headline[0];
  1285.         }
  1286.         }
  1287.     }
  1288.     
  1289.     if (stand_on) putp(exit_standout_mode);
  1290.     fclose(f);
  1291.     }
  1292.  
  1293.     prompt_line = Lines-1;    /* move prompt to last line */
  1294.     
  1295.     if (!no_conf && (modes & CONFIRMATION)) 
  1296.     any_key(prompt_line);
  1297. }
  1298.  
  1299.  
  1300. /*VARARGS*/
  1301. user_error(va_alist)
  1302. va_dcl
  1303. {
  1304.     char *fmt;
  1305.     va_list ap;
  1306.     
  1307.     unset_raw();
  1308.     clrdisp();
  1309.     fl;
  1310.  
  1311.     va_start(ap);
  1312.     fmt = va_arg1(char *);
  1313.     
  1314.     vprintf(fmt, va_args2toN);
  1315.     putchar(NL);
  1316.  
  1317.     va_end(ap);
  1318.     nn_exit(1);
  1319. }
  1320.  
  1321. /*VARARGS*/
  1322. msg(va_alist)
  1323. va_dcl
  1324. {
  1325.     va_list ap;
  1326.     
  1327.     va_start(ap);
  1328.     vmsg(va_args1toN);
  1329.     va_end(ap);
  1330. }
  1331.  
  1332. vmsg(va_tail)
  1333. va_tdcl
  1334. {
  1335.     static char errmsg[512] = "";
  1336.     char *fmt;
  1337.     
  1338.     fmt = va_arg1(char *);
  1339.     
  1340.     if (fmt) vsprintf(errmsg, fmt, va_args2toN);
  1341.  
  1342.     gotoxy(0, Lines-1);
  1343.     fputs(errmsg, stdout);
  1344.     clrline();
  1345.     any_message = 1;
  1346.     
  1347.     gotoxy(prompt_length, prompt_line);
  1348.     fl;
  1349. }
  1350.  
  1351. clrmsg(col)
  1352. int col;
  1353. {
  1354.     gotoxy(0, prompt_line + 1);
  1355.     clrpage(prompt_line + 1);
  1356.     if (col >= 0)
  1357.     gotoxy(prompt_length + col, prompt_line);
  1358.     fl;
  1359.     any_message = 0;
  1360. }
  1361.  
  1362.  
  1363. /*VARARGS*/
  1364. prompt(va_alist)
  1365. va_dcl
  1366. {
  1367.     register char *cp;
  1368.     int stand_on;
  1369.     char *fmt;
  1370.     static char cur_p[FILENAME];
  1371.     static char saved_p[FILENAME];
  1372.     va_list ap;
  1373.     
  1374.     va_start(ap);
  1375.     
  1376.     fmt = va_arg1(char *);
  1377.  
  1378.     if (fmt == P_VERSION) {
  1379.     gotoxy(0, prompt_line + 1);
  1380.     print_version("Release %R.%V, Patch Level %P, Compilation %U ");
  1381.     clrline();
  1382.     any_message++;
  1383.     
  1384.     if (prompt_line >= 0)
  1385.         gotoxy(prompt_length, prompt_line);
  1386.     return;
  1387.     }
  1388.     
  1389.     if (fmt == P_SAVE) {
  1390.     strcpy(saved_p, cur_p);
  1391.     return;
  1392.     }
  1393.     
  1394.     if (fmt == P_RESTORE)
  1395.     strcpy(cur_p, saved_p);
  1396.     
  1397.     if (prompt_line >= 0)
  1398.     gotoxy(0, prompt_line);
  1399.  
  1400.     if (fmt == P_MOVE) {
  1401.     clrline();
  1402.     return;
  1403.     }
  1404.  
  1405.     if (fmt != P_REDRAW && fmt != P_RESTORE) 
  1406.     vsprintf(cur_p, fmt, va_args2toN);
  1407.  
  1408.     putchar(CR);
  1409.     
  1410.     for (cp = cur_p, stand_on = 0, prompt_length = 0; *cp; cp++) {
  1411.     if (*cp == '\1') {
  1412.         if (cp[1] != '\1') {
  1413.         if (STANDOUT) {
  1414.             putp(stand_on ? exit_standout_mode : enter_standout_mode);
  1415.             stand_on = !stand_on;
  1416.             prompt_length += cookie_size;
  1417.         }
  1418.         continue;
  1419.         }
  1420.         cp++;
  1421.     } else
  1422.     if (*cp == '\2') {
  1423.         time_t t;
  1424.         char   *timestr;
  1425.         
  1426. #ifdef STATISTICS
  1427.         tick_usage(&t, (time_t *)NULL);
  1428. #endif        
  1429.  
  1430.         if (show_current_time) {
  1431. #ifndef STATISTICS        
  1432.         time(&t);
  1433. #endif
  1434.  
  1435.         timestr = ctime(&t) + 11;
  1436.         timestr[5] = NUL;
  1437.  
  1438.         printf("-- %s ", timestr);
  1439.         prompt_length += 9;
  1440.         }
  1441.  
  1442.         if (unread_mail(t)) {
  1443.         printf("Mail ");
  1444.         prompt_length += 5;
  1445.         }
  1446.         
  1447.         continue;
  1448.     }
  1449.  
  1450.     putchar(*cp);
  1451.     prompt_length ++;
  1452.     }
  1453.     if (stand_on) {
  1454.     putp(exit_standout_mode);
  1455.     prompt_length += cookie_size;
  1456.     }
  1457.     
  1458.     clrline();
  1459.  
  1460.     if (fmt == P_RESTORE) 
  1461.     restore_xy();
  1462.     else
  1463.     curxy_c = -1;
  1464. }
  1465.  
  1466.  
  1467. any_key(line)
  1468. int line;
  1469. {
  1470.     int was_raw, c, dmp;
  1471.  
  1472.     was_raw = is_raw;
  1473.     if (!is_raw) raw();
  1474.     if (line == 0)
  1475.     line = -1;
  1476.     else
  1477.     if (line < 0)
  1478.         line = Lines + line;
  1479.     
  1480.     if (line != 10000)
  1481.     so_printxy(0, line, "Hit any key to continue");
  1482.  
  1483.     clrline();
  1484.     
  1485.     dmp = do_macro_processing;
  1486.     do_macro_processing = 0;
  1487.     c = get_c();
  1488.     do_macro_processing = dmp;
  1489.  
  1490.     if (!was_raw) unset_raw();
  1491.     
  1492.     return c;
  1493. }
  1494.  
  1495.  
  1496. static pg_fline, pg_width, pg_maxw, pg_line, pg_col, pg_quit;
  1497.  
  1498. pg_init(first_line, cols)
  1499. int first_line, cols;
  1500. {
  1501.     pg_fline = first_line;
  1502.     pg_line = pg_fline - 1;
  1503.     pg_quit = pg_col = 0;
  1504.     pg_width = Columns / cols;
  1505.     pg_maxw = pg_width * (cols - 1);
  1506. }
  1507.  
  1508. pg_scroll(n)
  1509. int n;
  1510. {
  1511.     pg_line += n;
  1512.     if (pg_line >= (Lines - 1)) {
  1513.     pg_line = 0;
  1514.     return any_key(0) == K_interrupt;
  1515.     }
  1516.     return 0;
  1517. }
  1518.  
  1519. pg_next()
  1520. {
  1521.     int c;
  1522.     
  1523.     pg_line++;
  1524.     if (pg_line < Lines) {
  1525.     gotoxy(pg_col, pg_line);
  1526.     if (pg_line == Lines - 1 && pg_col == pg_maxw) {
  1527.         c = any_key(0);
  1528.         gotoxy(0, pg_fline);
  1529.         clrpage(pg_fline);
  1530.         pg_col = 0;
  1531.         pg_line = pg_fline;
  1532.         if (c == K_interrupt) {
  1533.         pg_quit = 1;
  1534.         return -1;
  1535.         }
  1536.         return 1;
  1537.     }
  1538.     } else {
  1539.     pg_line = pg_fline;
  1540.     pg_col += pg_width;
  1541.     gotoxy(pg_col, pg_line);
  1542.     }
  1543.     return 0;
  1544. }
  1545.  
  1546. pg_indent(pos)
  1547. int pos;
  1548. {
  1549.     gotoxy(pg_col + pos, pg_line);
  1550. }
  1551.  
  1552. pg_end()
  1553. {
  1554.     if (pg_quit == 0 && pg_next() == 0)
  1555.     any_key(0);
  1556. }
  1557.  
  1558.     
  1559. user_delay(ticks)
  1560. int ticks;
  1561. {
  1562.     if (ticks <= 0 || conf_dont_sleep) {
  1563.     printf(" <>");
  1564.     any_key(10000);
  1565.     } else {
  1566.     fl;
  1567.     sleep(ticks);
  1568.     }
  1569. }
  1570.  
  1571.